#include "dbscan.h"
#include <boost/foreach.hpp>

namespace Clustering
{

		
	void DBSCAN::run_cluster(Metrics::Distance<Metrics::MeterDistance<Clustering::Point>> & d, long double thresholdGeo, long double thresholdTime) 
	{
		
		ClusterId cid = 1;
		// foreach pid
		unsigned int psSize = _ps.get_num();

		cout << "nbPoints : " << psSize << endl;

		for (PointId pid = 0; pid < psSize; pid++)
		{
			
			// not already visited
			if (!(*_visited.get(pid))){  
				
				bool boolValueVisited = new bool(true);
				_visited.set(pid, &boolValueVisited);
				
				// get the neighbors
				Neighbors ne = *findNeighbors(d, pid, _eps, thresholdGeo, thresholdTime);

				
				
				// not enough support -> mark as noice
				if (ne.get_num() < _minPts)
				{
					bool boolValueNoise = new bool(true);
					_noise.set(pid, &boolValueNoise);
				} 
				else 
				{
					std::cout << "Point i=" << pid << " can be expanded " << std::endl;// = true;

					// Add p to current cluster
					Cluster *clust = new Cluster(cid);
					ClusterPoints* c = clust->getClusterRef();              // a new cluster
					PointId *pidToInsert = new PointId(pid);
					c->insert(pidToInsert);   	// assign pid to cluster

					ClusterId* cidToInsert = new ClusterId(cid);
					_pointId_to_clusterId.set(*pidToInsert, cidToInsert);

					// go to neighbors
					unsigned int neighborsSize = ne.get_num();
					//for (unsigned int i = 0; i < neighborsSize; i++)
					PointId *nPid = ne.get_first();
					while(nPid != NULL)
					{
						//PointId *nPid = ne.get(i);
						
						// not already visited
						if (!(*_visited.get(*nPid)))
						{
							
							bool boolValueVisited2 = true;
							_visited.set(*nPid, &boolValueVisited2);

							// go to neighbors
							Neighbors ne1 = *findNeighbors(d, *nPid, _eps, thresholdGeo, thresholdTime);
							
							// enough support
							if (ne1.get_num() >= _minPts)
							{
								PointId* currentPid = ne1.get_first();
								
								while(currentPid != NULL){
									if( (!isPresent(&ne, currentPid)) && (*currentPid != pid) ){
										PointId *  currentPidToInsert = new PointId(*currentPid);
										ne.insert(currentPidToInsert);
									}
									currentPid = ne1.get_next();
								
								}
							}
						}

						// not already assigned to a cluster
						if ((*_pointId_to_clusterId.get(*nPid)) == -1)
						{
							PointId * nPidTemp = new PointId(*nPid);
							
							c->insert(nPidTemp);
							
							_pointId_to_clusterId.set(*nPid, &cid);
							
						}
						nPid = ne.get_next();
					}
					
					if(clust->getClusterRef()->get_num() > _minPts){
						_clusters.insert(clust);
						
						cid = *new ClusterId(cid+1);				
					}else{
						cid = *new ClusterId(cid);
					}
				}				

			} // if (!visited
			
		} // for
	}

	
};